using UnityEngine;
using System.Collections.Generic;


using Gamelogic.Extensions;

public class RoundLineMeshBuilder : MeshDataMeshBuilder
{
   [Header("Mesh Options")]
   public List<Vector3> points;
   public float width;
   public bool loop;

   //public int triangleCount = 5;
   public float trianglesPerRevolution = 12;

   protected override MeshData GetMeshData()
   {
      List<MeshData> meshes = new List<MeshData>();
      
      if (loop)
      {
         meshes.Add(CalculateQuadsLoop());
         meshes.AddRange(CalculateJointFansLoop());
      }
      else
      {
         meshes.Add(CalculateQuads());
         meshes.Add(CalculateStartCap());
         meshes.Add(CalculateEndCap());
         meshes.AddRange(CalculateJointFans());
      }

      return MeshData.Combine(meshes);
   }

   private MeshData CalculateQuads()
   {
      return new MeshData
      {
         vertices = CalculateQuadVertices(),
         triangles = CalculateQuadTriangles(),
         uvs = CalculateQuadUvs(false)
      };
   }

   private MeshData CalculateQuadsLoop()
   {
      return new MeshData
      {
         vertices = CalculateQuadVerticesLoop(),
         triangles = CalculateQuadTrianglesLoop(),
         uvs = CalculateQuadUvs(true)
      };
   }

   private List<Vector3> CalculateQuadVertices()
   {
      var vertices = new List<Vector3>();

      var direction = (points[1] - points[0]).normalized;
      var left = direction.PerpXY();

      vertices.Add3(
         points[0] + left * width / 2,
         points[0],
         points[0] - left * width / 2);

      for (int i = 0; i < points.Count - 2; i++)
      {

         CalculateJoin(
            points[i], points[i + 1], points[i + 2],
            out bool fanAtTop, out Vector3 intersection,
            out Vector3 fanStart, out Vector3 fanEnd);

         if (fanAtTop)
         {
            vertices.Add3(fanStart, points[i + 1], intersection);
            vertices.Add3(fanEnd, points[i + 1], intersection);
         }
         else
         {
            vertices.Add3(intersection, points[i + 1], fanStart);
            vertices.Add3(intersection, points[i + 1], fanEnd);
         }
      }

      int last = points.Count - 1;
      direction = (points[last] - points[last - 1]).normalized;
      left = direction.PerpXY();

      vertices.Add3(
         points[last] + left * width / 2,
         points[last],
         points[last] - left * width / 2);

      return vertices;
   }

   private List<Vector3> CalculateQuadVerticesLoop()
   {
      var vertices = new List<Vector3>();

      for (int i = 0; i < points.Count; i++)
      {
         var point0 = points[i];
         var point1 = points[(i + 1) % points.Count];
         var point2 = points[(i + 2) % points.Count];

         CalculateJoin(
            point0, point1, point2,
            out bool fanAtTop, out Vector3 intersection,
            out Vector3 fanStart, out Vector3 fanEnd);

         if (fanAtTop)
         {
            Debug.Log(fanStart + " " + point1 + " " + intersection);
            Debug.Log(fanEnd + " " + point1 + " " + intersection);

            vertices.Add3(fanStart, point1, intersection);
            vertices.Add3(fanEnd, point1, intersection);
         }
         else
         {
            Debug.Log(intersection + " " + point1 + " " + fanStart);
            Debug.Log(intersection + " " + point1 + " " + fanEnd);

            vertices.Add3(intersection, point1, fanStart);
            vertices.Add3(intersection, point1, fanEnd);
         }
      }

      return vertices;
   }

   private List<int> CalculateQuadTriangles()
   {
      var triangles = new List<int>();

      for (int i = 0; i < points.Count - 1; i++)
      {
         int p0 = i * 6;
         int p1 = i * 6 + 1;
         int p2 = i * 6 + 2;

         int p3 = i * 6 + 3;
         int p4 = i * 6 + 4;
         int p5 = i * 6 + 5;

         triangles.AddRange(MeshBuilderUtils.QuadTriangles(p1, p4, p3, p0));
         triangles.AddRange(MeshBuilderUtils.QuadTriangles(p2, p5, p4, p1));
      }

      return triangles;
   }

   private List<int> CalculateQuadTrianglesLoop()
   {
      var triangles = new List<int>();

      int vertexCount = 6 * points.Count;

      for (int i = 0; i < points.Count; i++)
      {//+ 3 because we do not have the three points for the start cap
         int p0 = (i * 6 + 0 + 3) % vertexCount;
         int p1 = (i * 6 + 1 + 3) % vertexCount;
         int p2 = (i * 6 + 2 + 3) % vertexCount;

         int p3 = (i * 6 + 3 + 3) % vertexCount;
         int p4 = (i * 6 + 4 + 3) % vertexCount;
         int p5 = (i * 6 + 5 + 3) % vertexCount;

         triangles.AddRange(MeshBuilderUtils.QuadTriangles(p1, p4, p3, p0));
         triangles.AddRange(MeshBuilderUtils.QuadTriangles(p2, p5, p4, p1));
      }

      return triangles;
   }

   private MeshData CalculateStartCap()
   {
      
      var point0 = points[0];
      var point1 = points[1];
      var direction = (point1 - point0).normalized;
      var left = direction.PerpXY();
      var right = -left;

      float startAngle = left.Atan2XY();
      float endAngle = right.Atan2XY();

      int triangleCount = Mathf.CeilToInt(0.5f * trianglesPerRevolution);

      var vertices = MeshBuilderUtils.SectorVertices(
         point0, width / 2, startAngle, endAngle, true, triangleCount);

      var triangles = MeshBuilderUtils.SectorTriangles(0, triangleCount, true);

      var center = Vector2.one / 2;
      var uvs = MeshBuilderUtils.SectorUvs(center, 0.5f, MeshBuilderUtils.Right1, MeshBuilderUtils.Right3, triangleCount, true);


      return new MeshData
      {
         vertices = vertices,
         triangles = triangles,
         uvs = uvs
      };
   }

   private MeshData CalculateEndCap()
   {
      
      var point0 = points[points.Count - 2];
      var point1 = points[points.Count - 1];
      var direction = (point1 - point0).normalized;
      var left = direction.PerpXY();
      var right = -left;

      float startAngle = left.Atan2XY();
      float endAngle = right.Atan2XY();

      int triangleCount = Mathf.CeilToInt(0.5f * trianglesPerRevolution);

      var vertices = MeshBuilderUtils.SectorVertices(
         point1, width / 2, startAngle, endAngle, false, triangleCount);

      var triangles = MeshBuilderUtils.SectorTriangles(0, triangleCount, false);

      var center = Vector2.one / 2; 
      var uvs = MeshBuilderUtils.SectorUvs(center, 0.5f, MeshBuilderUtils.Right3, MeshBuilderUtils.Right1, triangleCount, true);



      return new MeshData
      {
         vertices = vertices,
         triangles = triangles,
         uvs = uvs
      };
   }
   private List<MeshData> CalculateJointFans()
   {

      List<MeshData> meshes = new List<MeshData>();

      for (int i = 0; i < points.Count - 2; i++)
      {
         CalculateJoin(
            points[i], points[i + 1], points[i + 2],
            out bool fanAtTop, out _,
            out Vector3 fanStart, out Vector3 fanEnd);
         
         float startAngle = (fanStart - points[i + 1]).Atan2XY();
         float endAngle = (fanEnd - points[i + 1]).Atan2XY();

         float revolutions = Mathf.Abs(MeshBuilderUtils.GetAngleBetween(startAngle, endAngle, !fanAtTop)) / MeshBuilderUtils.Right4;
         int triangleCount = Mathf.CeilToInt(revolutions * trianglesPerRevolution);

         var vertices = MeshBuilderUtils.SectorVertices(points[i + 1], width / 2, startAngle, endAngle, !fanAtTop, triangleCount);

         var triangles = MeshBuilderUtils.SectorTriangles(0, triangleCount, !fanAtTop);

         var center = Vector2.one / 2;
         var uvs = MeshBuilderUtils.SectorUvs(center, 0.5f, startAngle, endAngle, triangleCount, !fanAtTop);


         meshes.Add(new MeshData { vertices = vertices, triangles = triangles, uvs = uvs });
      }

      return meshes;
   }

   private List<MeshData> CalculateJointFansLoop()
   {
      List<MeshData> meshes = new List<MeshData>();

      for (int i = 0; i < points.Count; i++)
      {
         var point0 = points[i];
         var point1 = points[(i + 1) % points.Count];
         var point2 = points[(i + 2) % points.Count];
         CalculateJoin(
            point0, point1, point2,
            out bool fanAtTop, out _,
            out Vector3 fanStart, out Vector3 fanEnd);

         float startAngle = (fanStart - point1).Atan2XY();
         float endAngle = (fanEnd - point1).Atan2XY();
         float revolutions = Mathf.Abs(MeshBuilderUtils.GetAngleBetween(startAngle, endAngle, !fanAtTop)) / MeshBuilderUtils.Right4;
         int triangleCount = Mathf.CeilToInt(revolutions * trianglesPerRevolution);
         var vertices = MeshBuilderUtils.SectorVertices(point1, width / 2, startAngle, endAngle, !fanAtTop, triangleCount);
         var triangles = MeshBuilderUtils.SectorTriangles(0, triangleCount, !fanAtTop);
         var center = Vector2.one / 2;
         var uvs = MeshBuilderUtils.SectorUvs(center, 0.5f, startAngle, endAngle, triangleCount, !fanAtTop);

         meshes.Add(
            new MeshData 
            { 
               vertices = vertices, 
               triangles = triangles, 
               uvs = uvs
            });
      }

      return meshes;
   }

   public void CalculateJoin(
      Vector3 p0, Vector3 p1, Vector3 p2, 
      out bool fanAtTop, out Vector3 intersection,
      out Vector3 fanStart, out Vector3 fanEnd)   
   {
      var direction0 = (p1 - p0).normalized;
      var direction1 = (p2 - p1).normalized;

      fanAtTop = PerpDotXY(direction0, direction1) < 0;

      var left0 = direction0.PerpXY();
      var left1 = direction1.PerpXY();

      if (fanAtTop)
      {
         Line line0 = new Line { offset = p0 - width / 2 * left0, direction = direction0 };
         Line line1 = new Line { offset = p1 - width / 2 * left1, direction = direction1 };

         intersection = MeshBuilderUtils.GetIntersection(line0, line1);

         meshDebug.AddDotXY(intersection, GLColor.Yellow);

         fanStart = p1 + left0 * width / 2;
         fanEnd = p1 + left1 * width / 2;
      }
      else
      {
         Line line0 = new Line { offset = p0 + width / 2 * left0, direction = direction0 };
         Line line1 = new Line { offset = p1 + width / 2 * left1, direction = direction1 };

         intersection = MeshBuilderUtils.GetIntersection(line0, line1);

         meshDebug.AddDotXY(intersection, GLColor.Yellow);

         fanStart = p1 - left0 * width / 2;
         fanEnd = p1 - left1 * width / 2;
      }
   }

   private float PerpDotXY(Vector3 v1, Vector3 v2)
   {
      return Vector3.Dot(v1.PerpXY(), v2);
   }

   private List<Vector2> CalculateQuadUvs(bool loop)
   {
      var uvs = new List<Vector2>();

      int segmentCount = loop ? points.Count : (points.Count - 1);

      for(int i = 0; i < segmentCount; i++)
      {
         uvs.Add3(new Vector2(0.5f, 1), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0));
         uvs.Add3(new Vector2(0.5f, 1), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0));
      }

      return uvs;
   }   
}